package epam.lab.spring.parser;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * DOM Parser of Bean Definitions
 * @author Iurii Miedviediev
 * @version 1.0 Build 21.08.2014
 */
public class BeanDOMParser implements BeanParser {

    private static final Logger LOGGER = LoggerFactory.getLogger(BeanDOMParser.class);
    private static final String REF = "ref";
    private static final String VALUE = "value";

    /**
     * Parse given xml file into bean definitions
     * @param pathToFile path to the xml file for parsing
     * @return map, connecting id of the bean to its definition
     */
    @Override
    public Map<String, BeanDefinition> parseBeanXml(String pathToFile) {

        Map<String, BeanDefinition> definitions = new HashMap<>();

        Document beanDocument = getBeanDocument(pathToFile);
        if(beanDocument == null) {
            throw new IllegalArgumentException("Wrong name of the Document");
        }
        NodeList nodeList = beanDocument.getElementsByTagName("bean");

        for(int pos = 0; pos < nodeList.getLength(); pos++) {
            Element current = (Element) nodeList.item(pos);
            definitions.put(current.getAttribute("id"), parseElement(current));
        }
        return definitions;
    }

    /**
     * Parse one Element into bean definition
     * @param current element to parse
     * @return complete Bean Definition
     */
    private BeanDefinition parseElement(Element current) {
        BeanDefinition def = new BeanDefinition();
        def.setaClass(current.getAttribute("class"));

        //parse constructor arguments
        NodeList constructor = current.getElementsByTagName("constructor-arg");
        for(int i = 0; i < constructor.getLength(); i++) {
            Element elem = (Element) constructor.item(i);
            if(elem.hasAttribute(REF)) {
                def.addConstructorArg(REF + "-" + elem.getAttribute(REF));
            } else if(elem.hasAttribute(VALUE)) {
                def.addConstructorArg(VALUE + "-" + elem.getAttribute(VALUE));
            }
        }

        //parse properties
        NodeList properties = current.getElementsByTagName("property");
        for(int j = 0; j < properties.getLength(); j++) {
            Element elem = (Element) properties.item(j);
            if(elem.hasAttribute(REF)) {
                def.addProperty(elem.getAttribute("name"), REF + "-" + elem.getAttribute(REF));
            } else if(elem.hasAttribute(VALUE)) {
                def.addProperty(elem.getAttribute("name"), VALUE + "-" + elem.getAttribute(VALUE));
            }
        }
        return def;
    }

    /**
     * Get the Document Object from the path to the xml file
     * @param pathToFile path to the xml file
     * @return Document object of the specified xml file
     */
    private Document getBeanDocument(String pathToFile) {
        try {
            File file = new File("src/main/resources/"+pathToFile);
            DocumentBuilder dBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document doc = dBuilder.parse(file);
            doc.getDocumentElement().normalize();
            return doc;
        } catch(IOException |  ParserConfigurationException | SAXException e) {
            LOGGER.error("error while getting document of the file", e);
        }
        return null;
    }
}
